<!--
 * @version: 
 * @Company:  
 * @Description: 
 * @FilePath: \src\components\Custom\MonacoEditor\index.vue
-->
<template>
  <div class="tool-bar" v-if="!diff">
    <a-select ref="select" v-model:value="language.value" style="width: 120px" :options="language.options" show-search placeholder="请选择语言" @change="changeLanguage"> </a-select>
    <a-select ref="select" v-model:value="theme.value" style="width: 120px" :options="theme.options" show-search placeholder="请选择主题" @change="changeTheme"> </a-select>
  </div>
  <div ref="monacoEditor" style="height: 500px"></div>
</template>
<script lang="ts">
  import { defineComponent, ref, onMounted, onUnmounted, nextTick, toRaw, reactive, toRefs, watch } from 'vue';
  import type { PropType } from 'vue';
  import * as monaco from 'monaco-editor';
  import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
  import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
  import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
  import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
  import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
  import { SelectTypes } from 'ant-design-vue/es/select';
  import { getLanguageSelect, initLang } from './language.tsx';
  const defaultValue = `function hello() {\n\talert('Hello world!');\n}`;
  export default defineComponent({
    name: 'monacoEditor',
    props: {
      modelValue: {
        type: String as PropType<string>,
        default: defaultValue,
      },
      diff: {
        type: String as PropType<string>,
        default: '',
      },
      lang: {
        type: String as PropType<string>,
        default: null,
      },
      theme: {
        type: String as PropType<string>,
        default: null,
      },
    },
    setup(props, { emit }) {
      let editor: any = ref(null),
        __prevent_trigger_change_event = false;
      let diffEditor: any = ref(null);
      const monacoEditor: any = ref(null);
      let registerLanguages; // 用于存放注册过的语言

      watch(
        () => props.modelValue,
        (newValue) => {
          const model = editor.getModel();
          if (newValue !== model.getValue()) {
            editor.pushUndoStop();
            __prevent_trigger_change_event = true;
            model.pushEditOperations(
              [],
              [
                {
                  range: model.getFullModelRange(),
                  text: newValue,
                },
              ],
            );
            editor.pushUndoStop();
            __prevent_trigger_change_event = false;
            /*
             * 格式化代码
             */
            editor?.getAction('editor.action.formatDocument').run();
          }
        },
      );

      const data = reactive({
        theme: {
          value: (props.theme || 'vs-dark') as string | undefined,
          options: [
            { label: 'vs', value: 'vs' },
            { label: 'hc-black', value: 'hc-black' },
            { label: 'vs-dark', value: 'vs-dark' },
          ] as SelectTypes['options'],
        },
        language: {
          value: (props.lang || 'javascript') as string | undefined,
          options: getLanguageSelect() as SelectTypes['options'],
        },
      });
      onMounted(() => {
        nextTick(() => {
          // 如果不是对比代码
          if (!props.diff) {
            init();
            emit('update:modelValue', props.modelValue || defaultValue);
          } else {
            initDiff();
          }
          // 注册语言快捷提示以及语法
          registerLanguages = initLang(monaco);
        });
      });
      onUnmounted(() => {
        /*
         *  为防止重复注册，把所有的注册都销毁
         */
        registerLanguages.forEach((item) => item?.registers?.dispose());
      });
      const init = () => {
        // 初始化编辑器，确保dom已经渲染
        editor = monaco.editor.create(monacoEditor.value, {
          value: props.modelValue || defaultValue, //编辑器初始显示文字
          language: data.language.value || 'javascript', //语言支持自行查阅demo
          theme: data.theme.value || 'vs-dark', //官方自带三种主题vs, hc-black, or vs-dark
          selectOnLineNumbers: true, //显示行号
          roundedSelection: false,
          readOnly: false, // 只读
          cursorStyle: 'line', //光标样式
          automaticLayout: true, //自动布局
          glyphMargin: true, //字形边缘
          useTabStops: false,
          wordBasedSuggestions: true,
          quickSuggestions: true,
          fontSize: 18, //字体大小

          formatOnPaste: true,
          formatOnType: true,
          quickSuggestionsDelay: 100, //代码提示延时
          fixedOverflowWidgets: true, // 超出编辑器大小的使用fixed属性显示
          // scrollBeyondLastLine: false, // 取消代码后面空白
          // minimap: {
          //   enabled: false, // 不要小地图
          // },
        });
        // 监听值的变化
        editor.onDidChangeModelContent(() => !__prevent_trigger_change_event && getVal());
        setTimeout(() => {
          /*
           * 格式化代码
           */
          editor?.getAction('editor.action.formatDocument').run();
        }, 200);
        (window as any).MonacoEnvironment = {
          getWorker(_, label) {
            if (label === 'json') {
              return new jsonWorker();
            }
            if (label === 'css' || label === 'scss' || label === 'less') {
              return new cssWorker();
            }
            if (label === 'html' || label === 'handlebars' || label === 'razor') {
              return new htmlWorker();
            }
            if (label === 'typescript' || label === 'javascript') {
              return new tsWorker();
            }
            return new editorWorker();
          },
        };
      };

      // 初始化对比的editor
      const initDiff = () => {
        let originalModel = monaco.editor.createModel(props.diff, data.language.value || 'javascript');
        let modifiedModel = monaco.editor.createModel(props.modelValue, data.language.value || 'javascript');
        diffEditor = monaco.editor.createDiffEditor(monacoEditor.value, {
          enableSplitViewResizing: false,
          automaticLayout: true,
          readOnly: true,
        });
        diffEditor.setModel({
          original: originalModel,
          modified: modifiedModel,
        });
      };
      /**
       * @vuese
       * @Description: 手动获取editor值
       * @arg {*}
       */
      const getVal = () => {
        emit('update:modelValue', editor.getValue());
        return toRaw(editor).getValue(); //获取编辑器中的文本
      };
      /**
       * @vuese
       * @Description: 修改主题
       * @arg {*} event
       */
      const changeTheme = (event) => {
        monaco.editor.setTheme(event);
      };
      /**
       * @vuese
       * @Description: 修改语言
       * @arg {*} event
       */
      const changeLanguage = (event) => {
        monaco.editor.setModelLanguage(editor.getModel(), event);
      };

      return {
        ...toRefs(data),
        diff: props.diff,
        editor,
        getVal,
        monacoEditor,
        changeTheme,
        changeLanguage,
      };
    },
  });
</script>
<style lang="less">
  .tool-bar {
    margin-bottom: 10px;
    > .ant-select {
      margin-right: 10px;
    }
  }
</style>
